#pragma once

#include <iostream>
#include <functional>
#include <sys/epoll.h>
#include "err.hpp"
#include "log.hpp"
#include "sock.hpp"

namespace epoll_ns
{
    static const int default_port = 8080;// 默认端口号
    static const int epoll_size = 128;// 默认epoll大小
    static const int default_value = -1;// 默认值
    static const int default_ev_num = 64;// 默认事件个数

    using func_t = std::function<std::string (const std::string&)>;

    class EpollServer
    {
    public:
        EpollServer(func_t cb, const uint16_t port = default_port, const int ev_num = default_ev_num)
            : _listen_sockfd(default_value), _port(port), _epfd(default_value), _revs(nullptr), _event_num(ev_num), _callback(cb)
        {}
        ~EpollServer()
        {
            if(_listen_sockfd != default_value) close(_listen_sockfd);
            if(_epfd != default_value) close(_epfd);
            if(_revs) delete[] _revs;
        }

        void initServer()
        {
            /* 1. 创建socket */

            // 创建套接字
            _listen_sockfd = Sock::Socket();
            // 绑定端口
            Sock::Bind(_listen_sockfd, _port);
            // 设置为监听状态
            Sock::Listen(_listen_sockfd);

            /* 2. 创建epoll模型 */
            _epfd = epoll_create(epoll_size);
            if(_epfd < 0)
            {
                logMessage(FATAL, "epoll create error: %s", strerror(errno));
                exit(EPOLL_CREATE_ERROR);
            }

            /* 3. 先将listensock交付给epoll */
            struct epoll_event ev;
            ev.events = EPOLLIN;// 关注读事件
            ev.data.fd = _listen_sockfd;// 当事件就绪，被重新取出的时候，要知道是哪一个fd就绪了

            epoll_ctl(_epfd, EPOLL_CTL_ADD, _listen_sockfd, &ev);

            /* 4. 为就绪事件申请空间 */
            _revs = new struct epoll_event[_event_num];

            logMessage(NORMAL, "init server success");
        }
        void start()
        {
            int timeout = -1;// ms

            while(true)
            {
                int n = epoll_wait(_epfd, _revs, _event_num, timeout);
                switch (n)
                {
                case 0:
                    logMessage(NORMAL, "timeout...");
                    break;
                case -1:
                    logMessage(WARNING, "epoll wait fail, errno: %d, indiacte: %s", errno, strerror(errno));
                    break;
                default:
                    logMessage(NORMAL, "have event ready");
                    // 处理事件
                    HandleEvent(n);
                    break;
                }
            }
        }
    private:
        void HandleEvent(int ready_ev_num/* 就绪事件个数 */)
        {
            logMessage(DEBUG, "start handle event");
            for(int i = 0; i < ready_ev_num; ++i)
            {
                uint32_t events = _revs[i].events;// 就绪事件
                int readyfd = _revs[i].data.fd;// 就绪fd

                if(readyfd == _listen_sockfd && (events & EPOLLIN))// listen套接字的读事件就绪
                {
                    // 获取新连接
                    std::string clientip;
                    uint16_t clientport;
                    int sockfd = Sock::Accept(_listen_sockfd, &clientip, &clientport);
                    if(sockfd < 0)
                    {
                        logMessage(WARNING, "Accept link error");
                        continue;
                    }
                    // 获取新连接成功，将新连接托管给epoll
                    struct epoll_event ev;
                    ev.events = EPOLLIN;// 只关心读事件
                    ev.data.fd = sockfd;
                    epoll_ctl(_epfd, EPOLL_CTL_ADD, sockfd, &ev);
                }
                else if(events & EPOLLIN)// 其他套接字的读事件就绪，即普通读事件就绪
                {
                    // read bug
                    char buffer[1024];
                    int n = recv(readyfd, buffer, sizeof(buffer) - 1, 0);
                    if(n > 0)
                    {
                        buffer[n] = 0;// 当作字符串
                        logMessage(DEBUG, "client>> %s", buffer);

                        std::string response = _callback(buffer);

                        send(readyfd, response.c_str(), response.size(), 0);// send bug
                    }
                    else if(n == 0)
                    {
                        // 删除时，建议先从epoll中移除fd，然后再关闭socket。因为移除是移除合法的sockfd，关闭后就不合法了。
                        epoll_ctl(_epfd, EPOLL_CTL_DEL, readyfd, nullptr);// 不再关心此文件描述符。\
                        红黑树结点是<K,V>结构，fd就是K，删除只需要知道fd即可
                        close(readyfd);

                        logMessage(NORMAL, "client quit");
                    }
                    else
                    {
                        // 删除时，建议先从epoll中移除fd，然后再关闭socket。因为移除是移除合法的sockfd，关闭后就不合法了。
                        epoll_ctl(_epfd, EPOLL_CTL_DEL, readyfd, nullptr);// 不再关心此文件描述符。\
                        红黑树结点是<K,V>结构，fd就是K，删除只需要知道fd即可
                        close(readyfd);

                        logMessage(ERROR, "recv error, errno: %d, indicate: %s", errno, strerror(errno));
                    }
                }
                else
                {
                    // TODO
                }
            }

            logMessage(DEBUG, "end handle event");
        }
    private:
        int _listen_sockfd;// 监听套接字
        uint16_t _port;// 端口号
        int _epfd;// epoll模型
        struct epoll_event* _revs;// 内核返回给用户的所有就绪的事件，是个数组
        int _event_num;// 事件最大个数
        func_t _callback;
    };
}